﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MyNetwork
{
    public class ServerSocketTcp
    {
        public event Action<ClientSocketTcp> Accepted;

        public string IP
        {
            get { return _ip; }
            set { _ip = value; }
        }
        private string _ip = "192.168.1.13";
        public int Port
        {
            get { return _port; }
            set { _port = value; }
        }
        private int _port = 12334;

        public int Capability
        {
            get { return _capability; }
            set { _capability = value; }
        }
        private int _capability = 1;

        private Socket listener;

        private void CloseServer()
        {
            listening = false;
            accepting = false;
            try
            {
                if (listener != null)
                {
                    listener.Close();
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }
            finally
            {
                listener = null;
            }

        }

        private bool _closing = false;
		public void RemoveClient(string obj)
		{
            if (_closing) return;
            lock (_listClient)
            {
                _listClient.Remove(obj);
                accepting = (_capability > 0 && _listClient.Count < _capability) ? true : false;
            }
        }

        public void Close()
        {
            _closing = true;
            CloseServer();

            lock (_listClient)
            {
                foreach (var item in _listClient)
                {
                    item.Value.Close();
                }

                _listClient.Clear();
            }
            _closing = false;
        }

        private bool listening = false;
        private bool accepting = false;
        public void Start()
        {
            CloseServer();
            try
            {
                IPAddress ipAddress = null;
                ipAddress = IPAddress.Parse(_ip);

                listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

                listener.Bind(new IPEndPoint(ipAddress, _port));
                listener.Listen(2);
            }
            catch (Exception e)
            {
                throw e;
            }

            listening = true;
            ThreadPool.QueueUserWorkItem(x =>
            {
                while (listening)
                {
                    if (accepting == false)
                    {
                        lock (_listClient)
                        {
                            accepting = true;
                            try
                            {

                                listener.BeginAccept(OnAccept, listener);
                                System.Diagnostics.Debug.WriteLine("listening one more");
                            }
                            catch (Exception ex)
                            {
                                System.Diagnostics.Debug.WriteLine("listening " + ex.Message);
                            }
                        }
                    }
                }
            });
        }

        private Dictionary<string, ClientSocketTcp> _listClient = new Dictionary<string, ClientSocketTcp>();
        private void OnAccept(IAsyncResult ar)
        {
            try
            {
                if (listening == false)
                {
                    accepting = false;
                    return;
                }

                Socket l = ar.AsyncState as Socket;
                if (l != null)
                {
                    Socket client = l.EndAccept(ar);
                    IPEndPoint iep = client.RemoteEndPoint as IPEndPoint;
                    string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
                    var cs = new ClientSocketTcp();
                    cs.Attach(client, iep.Address.ToString(), iep.Port);
                    OnComplete(key, cs);
                }
            }
            catch (Exception ex)
            {
                accepting = false;
                System.Diagnostics.Debug.WriteLine("AcceptCallBack " + ex.Message);
            }
        }

        private void OnComplete(string key, ClientSocketTcp client)
        {
            lock (_listClient)
            {
                if (!_listClient.ContainsKey(key))
                {
                    _listClient.Add(key, client);
                    if (Accepted != null) Accepted(client);
                }
                accepting = (_capability > 0 && _listClient.Count < _capability) ? true : false;
            }
        }
    }
}
